package com.cpoopc.fuckapp.utils.security;

import android.util.Log;

import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

/**
 * 提供RSA加密支持
 * <p>
 * Description:描述
 * </p>
 */
public class RSA {

    private static final String TAG = "RSA";
    /** 指定加密算法为RSA */
    private static String ALGORITHM = "RSA";

    /** 指定key的大小 */
    private static int KEYSIZE = 1024;

    /**	
     * 对输入进行Rsa加密
     * @param publicKey 用于生成RSA加密所需的公钥
     * @param info 被加密文本，如果为null则返回null
     * @return 加密后文本，如果为null说明输入为null
     * @throws SecurityException 加密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] encrypt(RSAPublicKey publicKey, String info) throws SecurityException {
        if (null == publicKey) {
            throw new IllegalArgumentException(SecurityUtil.ERROR_KEY_IS_NULL);
        }
        if (null == info || info.equals("")) {
            return null;
        }
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] b = info.getBytes();
            byte[] b1 = cipher.doFinal(b);
            return b1;
        } catch (NoSuchAlgorithmException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("NoSuchAlgorithmException");
        } catch (NoSuchPaddingException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("NoSuchPaddingException");
        } catch (InvalidKeyException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("InvalidKeyException");
        } catch (IllegalBlockSizeException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("IllegalBlockSizeException");
        } catch (BadPaddingException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("BadPaddingException");
        }
    }

    /**
     * 对输入进行Rsa加密
     * @param publicKey 用于RSA加密所需的公钥
     * @param info 被加密文本，如果为null则返回null
     * @return 加密后文本，如果为null说明输入为null
     * @throws SecurityException 加密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] encrypt(RSAPublicKey publicKey, byte[] info) throws SecurityException {
        return encrypt(publicKey, SecurityUtil.toHexString(info));
    }

    /**
     * 对输入进行Rsa加密
     * @param filePath 存放公钥的文件
     * @param info 被加密文本，如果为null则返回null
     * @return 加密后文本，如果为null说明输入为null
     * @throws SecurityException 加密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] encrypt(String filePath, byte[] info) throws SecurityException {
        RSAPublicKey key = (RSAPublicKey) SecurityUtil.getObjFromFile(filePath);
        return encrypt(key, info);
    }

    /**
     * 对输入进行Rsa加密
     * @param filePath 存放公钥的文件
     * @param info 被加密文本，如果为null则返回null
     * @return 加密后文本，如果为null说明输入为null
     * @throws SecurityException 加密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] encrypt(String filePath, String info) throws SecurityException {
        RSAPublicKey key = (RSAPublicKey) SecurityUtil.getObjFromFile(filePath);
        return encrypt(key, info);
    }

    /**
     * 对输入进行Rsa加密
     * @param stream 存放公钥的流
     * @param info 被加密文本，如果为null则返回null
     * @return 加密后文本，如果为null说明输入为null
     * @throws SecurityException 加密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] encrypt(InputStream stream, byte[] info) throws SecurityException {
        RSAPublicKey key = (RSAPublicKey) SecurityUtil.getObjFromStream(stream);
        return encrypt(key, info);
    }

    /**
     * 对输入进行Rsa加密
     * @param stream 存放公钥的流
     * @param info 被加密文本，如果为null则返回null
     * @return 加密后文本，如果为null说明输入为null
     * @throws SecurityException 加密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] encrypt(InputStream stream, String info) throws SecurityException {
        RSAPublicKey key = (RSAPublicKey) SecurityUtil.getObjFromStream(stream);
        return encrypt(key, info);
    }

    /**
     * 对输入进行Rsa解密
     * @param privateKey RSA解密所需私钥
     * @param info 被解密文本，如果为null则返回null
     * @return 解密后文本，如果为null说明输入为null
     * @throws SecurityException 解密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] decrypt(RSAPrivateKey privateKey, byte[] info) throws SecurityException {
        if (null == privateKey) {
            throw new IllegalArgumentException(SecurityUtil.ERROR_KEY_IS_NULL);
        }
        if (null == info || info.equals("")) {
            return null;
        }
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(info);
        } catch (NoSuchAlgorithmException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("NoSuchAlgorithmException");
        } catch (NoSuchPaddingException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("NoSuchPaddingException");
        } catch (InvalidKeyException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("InvalidKeyException");
        } catch (IllegalBlockSizeException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("IllegalBlockSizeException");
        } catch (BadPaddingException e) {
            Log.w(TAG, "" + e.getMessage());
            throw new SecurityException("BadPaddingException");
        }
    }

    /**
     * 对输入进行Rsa解密
     * @param privateKey RSA解密所需私钥
     * @param info 被解密文本，如果为null则返回null
     * @return 解密后文本，如果为null说明输入为null
     * @throws SecurityException 解密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] decrypt(RSAPrivateKey privateKey, String info) throws SecurityException {
        return decrypt(privateKey, SecurityUtil.convertHexString(info));
    }

    /**
     * 对输入进行Rsa解密
     * @param file 存放RSA解密所需私钥的文件
     * @param info 被解密文本，如果为null则返回null
     * @return 解密后文本，如果为null说明输入为null
     * @throws SecurityException 解密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] decrypt(String file, String info) throws SecurityException {
        RSAPrivateKey privateKey = (RSAPrivateKey) SecurityUtil.getObjFromFile(file);
        return decrypt(privateKey, SecurityUtil.convertHexString(info));
    }

    /**
     * 对输入进行Rsa解密
     * @param file 存放RSA解密所需私钥的文件
     * @param info 被解密文本，如果为null则返回null
     * @return 解密后文本，如果为null说明输入为null
     * @throws SecurityException 解密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] decrypt(String file, byte[] info) throws SecurityException {
        RSAPrivateKey privateKey = (RSAPrivateKey) SecurityUtil.getObjFromFile(file);
        return decrypt(privateKey, info);
    }

    /**
     * 对输入进行Rsa解密
     * @param stream 存放RSA解密所需私钥的流
     * @param info 被解密文本，如果为null则返回null
     * @return 解密后文本，如果为null说明输入为null
     * @throws SecurityException 解密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] decrypt(InputStream stream, String info) throws SecurityException {
        RSAPrivateKey privateKey = (RSAPrivateKey) SecurityUtil.getObjFromStream(stream);
        return decrypt(privateKey, SecurityUtil.convertHexString(info));
    }

    /**
     * 对输入进行Rsa解密
     * @param stream 存放RSA解密所需私钥的流
     * @param info 被解密文本，如果为null则返回null
     * @return 解密后文本，如果为null说明输入为null
     * @throws SecurityException 解密失败时抛出异常,具体异常通过getMessage查看
     */
    public static byte[] decrypt(InputStream stream, byte[] info) throws SecurityException {
        RSAPrivateKey privateKey = (RSAPrivateKey) SecurityUtil.getObjFromStream(stream);
        return decrypt(privateKey, info);
    }
    
    /**	
     * 生成Rsa所需KeyPair,通过keypair.getPublic()/getPrivate()可以获得Rsa加密解密所需的公钥和私钥
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair createKeyPair() throws NoSuchAlgorithmException {
        SecureRandom sr = new SecureRandom();
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM);
        kpg.initialize(KEYSIZE, sr);
        return kpg.generateKeyPair();
    }

}
